package com.atan.xiaohongshu.auth.runner;

import cn.hutool.core.collection.CollUtil;
import com.atan.framework.common.util.JsonUtils;
import com.atan.xiaohongshu.auth.constant.RedisConstants;
import com.atan.xiaohongshu.auth.domain.dataobject.PermissionDO;
import com.atan.xiaohongshu.auth.domain.dataobject.RoleDO;
import com.atan.xiaohongshu.auth.domain.dataobject.RolePermissionDO;
import com.atan.xiaohongshu.auth.domain.mapper.PermissionDOMapper;
import com.atan.xiaohongshu.auth.domain.mapper.RoleDOMapper;
import com.atan.xiaohongshu.auth.domain.mapper.RolePermissionDOMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class PushRolePermission2RedisRunner implements ApplicationRunner {

    @Autowired
    private RoleDOMapper roleDOMapper;
    @Autowired
    private RolePermissionDOMapper rolePermissionDOMapper;
    @Autowired
    private PermissionDOMapper permissionDOMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 权限同步标记 Key
    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";

    @Override
    public void run(ApplicationArguments args) throws Exception {

        log.info("开始推送角色权限到Redis...");

        try {
            // 是否能够同步数据: 原子操作，只有在键 PUSH_PERMISSION_FLAG 不存在时，才会设置该键的值为 "1"，并设置过期时间为 1 天
            boolean canPushed = redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS);
            if (!canPushed) {
                log.warn("==> 角色权限数据已经同步至 Redis 中，不再同步...");
                return;
            }
            //查询所有被启用的角色
            List<RoleDO> roleDOList = roleDOMapper.selectEnabledList();
            if (CollUtil.isNotEmpty(roleDOList)) {
                //角色ID集合
                List<Long> roleIdList = roleDOList.stream().map(RoleDO::getId).collect(Collectors.toList());

                //根据角色id批量查询角色权限
                List<RolePermissionDO> rolePermissionDOS = rolePermissionDOMapper.selectByRoleIds(roleIdList);
                // 按角色 ID 分组, 每个角色 ID 对应多个权限 ID
                Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionDOS.stream().collect(
                        Collectors.groupingBy(RolePermissionDO::getRoleId,
                                Collectors.mapping(RolePermissionDO::getPermissionId, Collectors.toList()))
                );

                // 查询 APP 端所有被启用的权限
                List<PermissionDO> permissionDOS = permissionDOMapper.selectAppEnabledList();
                // 权限 ID - 权限 DO
                Map<Long, PermissionDO> permissionIdDOMap = permissionDOS.stream().collect(
                        Collectors.toMap(PermissionDO::getId, permissionDO -> permissionDO)
                );
                // 组织 角色ID-权限 关系
                /**
                 * 这里应该优化成String,List<String>
                 */
                Map<String, List<String>> roleIdPermissionDOMap = Maps.newHashMap();

                // 循环所有角色
                roleDOList.forEach(roleDO -> {
                    // 当前角色 ID
                    Long roleId = roleDO.getId();
                    // 当前角色 roleKey
                    String roleKey = roleDO.getRoleKey();
                    // 当前角色 ID 对应的权限 ID 集合
                    List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
                    if (CollUtil.isNotEmpty(permissionIds)) {
//                        List<PermissionDO> perDOS = Lists.newArrayList();
                        List<String> permissionKeys = Lists.newArrayList();
                        permissionIds.forEach(permissionId -> {
                            // 根据权限 ID 获取具体的权限 DO 对象
                            PermissionDO permissionDO = permissionIdDOMap.get(permissionId);
                            if (Objects.nonNull(permissionDO)) {
                                permissionKeys.add(permissionDO.getPermissionKey());
                            }
                        });
                        roleIdPermissionDOMap.put(roleKey, permissionKeys);
                    }
                });

                /**
                 * 由于网关层面鉴权权限的时候，更需要的是 ["app:note:publish", "app:comment:publish"]
                 * 这种格式的数据，并且之前key的标识为roleId，但是现在角色的value为roleKey
                 * 因此把权限的key  改为roleKey value直接改成  ["app:note:publish", "app:comment:publish"]
                 * 这样在网关层面好获取数据
                 */
                // 同步至 Redis 中，方便后续网关查询鉴权使用
                roleIdPermissionDOMap.forEach((roleKey, permissionDO) -> {
                    String key = RedisConstants.buildRolePermissionsKey(roleKey);
                    redisTemplate.opsForValue().set(key, permissionDO);
                });
                log.info("推送角色权限到Redis完成。");
            }

        } catch (Exception e) {
            log.error("==> 同步角色权限数据到 Redis 中失败: ", e);
            redisTemplate.delete(PUSH_PERMISSION_FLAG);
        }

    }

}

